package edu.ua.bmr.android;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.AlertDialog.Builder;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

/**
 * This activity represents the user's current schedule for the currently
 * selected term. Here, the user can drop classes and navigate to the search
 * screen.
 * 
 * @author OEP
 * 
 */
public class SemesterActivity extends ListActivity implements Runnable {
	private static String TAG = "SemesterActivity";

	public static final int REQUEST_SEARCH = 0;
	public static final int REQUEST_VIEW = 1;

	/** Data for the Class ListView */
	private ArrayList<HashMap<String, String>> mData = new ArrayList<HashMap<String, String>>();

	/** Data for the Semester switch dialog */
	private ArrayList<HashMap<String, String>> mSemesters;

	/** Tells the user to wait */
	private Dialog mWaitDialog;

	/** The semester-switching dialog */
	private Dialog mSwitchDialog;
	private TextView mFooter;
	
	private OnClickListener mSwitchSemesterClickListener = new OnClickListener() {
		public void onClick(View v) {
			showTermSwitcher();
		}
	};
	
	private OnClickListener mAddClassClickListener = new OnClickListener() {
		public void onClick(View v) {
			searchCatalog();
		}
	};
	
	private OnClickListener mConfirmScheduleClickListener = new OnClickListener() {
		public void onClick(View v) {
			mWaitDialog = BMR.showProgressDialog(SemesterActivity.this, R.string.please_wait,
					R.string.communicating_with_server);
			Thread t = new Thread( mConfirmSchedule );
			t.start();
		}
	};

	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		Log.v( "SemesterActivity", "onCreate()" );
		this.setContentView(R.layout.act_semester);
		initUI();
		recache();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.v( "SemesterActivity", "onDestroy()" );

		// Ben: IMHO, no need to ever explicitly logout.
		// Session s = Session.getInstance();
		// s.logout();
	}

	/**
	 * Add the appropriate ListView headers and Listeners.
	 */
	private void initUI() {
		// Grab the Activity's ListView
		ListView list = getListView();

		// Set the title to something cool!
		updateTitle();
		
		// Add the "Add class" footer
		mFooter = new TextView(this);
		mFooter.setTextSize(25);
		mFooter.setPadding(20, 20, 20, 20);
		mFooter.setTextColor(Color.GRAY);
	
		// Change the label in case this semester isn't addable.
		int label = (Session.getInstance().isAddable()) ? R.string.add_new_class : R.string.search_catalog;
		mFooter.setText(label);
		
		mFooter.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
		list.addFooterView(mFooter);
	}

	/**
	 * Handles viewing the class when clicked or searching for classes if the
	 * user wants to add a new class.
	 */
	protected void onListItemClick(ListView l, View v, int position, long id) {

		if ( l.getHeaderViewsCount() > 0 && position == 0) {
			return;
		}
		
		// Did we click the trailer view?
		// Go to add class screen!
		if (position == l.getAdapter().getCount() - 1) {
			searchCatalog();
			return;
		}

		// We clicked a class. We should pass this object
		// to ClassActivity.
		HashMap<String, String> map = (HashMap<String, String>) l
				.getItemAtPosition(position);

		// Construct the intent needed for viewing a class...
		Intent intent = BMR.getClassViewIntent(SemesterActivity.this, map);

		// We obviously are registered for this class.
		intent.putExtra(BMR.KEYWORD_REGISTERED, "1");

		// Start the next activity.
		startActivityForResult(intent, REQUEST_VIEW);
	}

	/** Thread for confirming schedule */
	private Runnable mConfirmSchedule = new Runnable() {
		public void run() {
			Session s = Session.getInstance();

			try {
				// Attempt to confirm.
				Bundle b = s.confirmSemester();

				// Prepare thy message!
				Message msg = new Message();
				
				// Stuff the bundle values in the Message
				msg.setData(b);

				// No exceptions thrown.
				msg.arg1 = BMR.MESSAGE_LEGAL;
				mConfirmScheduleHandler.sendMessage(msg);
			} catch (BMRException e) {
				// Exception thrown. Make a message that carries
				// the proper string resource to display to the user.
				Message msg = new Message();
				msg.arg1 = BMR.MESSAGE_EXCEPTION;
				msg.arg2 = e.getResourceId();

				mConfirmScheduleHandler.sendMessage(msg);
			}
		}
	};

	/**
	 * Handles messages passed the confirm schedule thread.
	 */
	private Handler mConfirmScheduleHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			mWaitDialog.dismiss();

			if (msg.arg1 == BMR.MESSAGE_LEGAL) {
				Bundle b = msg.getData();
				
				// Grab the Bundled data
				boolean result = b.getBoolean(BMR.KEYWORD_RESULT);
				String text = b.getString(BMR.KEYWORD_MSG);
				
				// Toast out a message to the user telling what happened.
				if(result) {
					Toast.makeText(SemesterActivity.this, R.string.successful_confirm, Toast.LENGTH_SHORT ).show();
				}
				else {
					Toast.makeText(SemesterActivity.this, text, Toast.LENGTH_SHORT).show();
				}
				
				updateUI();
				
			} else {
				// An exception was thrown. Build a dialog to tell the user.
				Builder builder = new AlertDialog.Builder(SemesterActivity.this);
				builder.setTitle(R.string.alert_error);
				builder.setMessage(msg.arg2);
				builder.setPositiveButton(R.string.button_ok, null);
				builder.show();
			}
		}
	};

	/** Thread for term-switching dialog */
	private Runnable mFetchSemesters = new Runnable() {
		public void run() {
			Session s = Session.getInstance();

			try {
				// See if we can stick some semesters in there..
				mSemesters = s.fetchSemesterList();

				// Prepare thy message!
				Message msg = new Message();

				// A success!
				msg.arg1 = BMR.MESSAGE_SUCCESS;
				mSemesterListHandler.sendMessage(msg);
			} catch (BMRException e) {
				// Oh poop. Make a message.
				Message msg = new Message();
				msg.arg1 = BMR.MESSAGE_EXCEPTION;
				msg.arg2 = e.getResourceId();

				mSemesterListHandler.sendMessage(msg);
			}
		}
	};

	/**
	 * Handles messages passed by our login thread.
	 */
	private Handler mSemesterListHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			mWaitDialog.dismiss();

			// See if we got a success message, then show a dialog.
			if (msg.arg1 == BMR.MESSAGE_SUCCESS) {
				SemesterActivity.this.showDialog(0);
			}
			
			// Check for exception message
			else if(msg.arg1 == BMR.MESSAGE_EXCEPTION) {
				// An exception was thrown. Build a dialog to tell the user.
				Builder builder = new AlertDialog.Builder(SemesterActivity.this);
				builder.setTitle(R.string.alert_error);
				builder.setMessage(msg.arg2);
				builder.setPositiveButton(R.string.button_ok, null);
				builder.show();
			}
			
			// Message passing is broken!
			else {
				throw new IllegalArgumentException("Semester fetcher passed an illegal message.");
			}
		}
	};

	private OnItemClickListener mSemesterSelected = new OnItemClickListener() {

		public void onItemClick(AdapterView parent, View view, int position,
				long id) {
			Session s = Session.getInstance();
			ListView list = (ListView) mSwitchDialog
					.findViewById(R.id.semester_select_list);

			// We will receive a hashmap containing lots of data about the newly
			// selected semester
			HashMap<String, String> map = (HashMap<String, String>) list
					.getItemAtPosition(position);

			// Update the semester ID
			try {
				s.setSemesterId(map.get(BMR.TABLE_SEMESTER_ID));
			} catch (BMRException e) {
				// TODO: We probably should do something about this exception
				// here,
				// but at this point, the semester data MUST be cached so the
				// exception
				// won't be thrown.
				e.printStackTrace();
			}

			// Update the title
			updateTitle();

			mSwitchDialog.dismiss();
			SemesterActivity.this.recache();
		}
	};

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menu_search_catalog:
			searchCatalog();
			return true;

		case R.id.menu_switch_semester:
			showTermSwitcher();
			return true;
		}
		return false;
	}

	private void showTermSwitcher() {
		if (mSemesters == null) {
			// We need to fetch the semesters this user can switch to. It might
			// take some time.
			mWaitDialog = BMR.showProgressDialog(this, R.string.please_wait,
					R.string.communicating_with_server);
			Thread t = new Thread(mFetchSemesters);
			t.start();
		} else {
			// Just go ahead and do it.
			// mSwitchDialog.show();
			showDialog(0);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu_semester, menu);
		return true;
	}

	@Override
	public void onActivityResult(int request, int result, Intent intent) {
		if (result == ClassActivity.RESULT_DIRTY) {
			Log.i(TAG, "Data is dirty. Recaching.");
			recache();
		}
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		// Set up the semester switching dialog
		// Add its listeners
		// Set its layout
		mSwitchDialog = new Dialog(this);
		
		try {
			if ( mSemesters == null ) {
				Session s = Session.getInstance();
				mSemesters = s.fetchSemesterList();
			}
			mSwitchDialog.setTitle(R.string.select_a_semester);
			mSwitchDialog.setContentView(R.layout.dialog_semesters);
			ListView semesterList = (ListView) mSwitchDialog
					.findViewById(R.id.semester_select_list);
			semesterList.setOnItemClickListener(mSemesterSelected);
			
			semesterList.setAdapter(new SimpleAdapter(SemesterActivity.this, mSemesters,
					R.layout.item_semester, new String[] { "name" },
					new int[] { android.R.id.text1 }));
	
		}
		catch ( Exception ex ) {
			Log.v( "SemesterActivity", ex.getMessage() );
			mSwitchDialog.setTitle("Failed to get semester list" );
		}
		

		return mSwitchDialog;
	}

	private void updateTitle() {
		Resources r = getResources();
		Session s = Session.getInstance();
		setTitle(r.getString(R.string.classes_for) + " " + s.getUsername() + ": " + s.getSemesterName());
	}

	/**
	 * Just an alias for starting the search classes activity.
	 */
	private void searchCatalog() {
		Intent i = new Intent(this, SearchActivity.class);
		startActivityForResult(i, REQUEST_SEARCH);
	}

	/**
	 * Handles messages passed by our login thread.
	 */
	private Handler mClassListHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			mWaitDialog.dismiss();

			// No exceptions thrown. We have a success.
			if (msg.arg1 == BMR.MESSAGE_SUCCESS) {
				updateUI();
			} 
			// An Exception was thrown. Handle it.
			else if(msg.arg2 == BMR.MESSAGE_EXCEPTION) {
				// An exception was thrown. Build a dialog to tell the user.
				Builder builder = new AlertDialog.Builder(SemesterActivity.this);
				builder.setTitle(R.string.alert_error);
				builder.setMessage(msg.arg2);
				builder.setPositiveButton(R.string.button_ok, null);
				builder.show();
			}
			// Someone did something naughty.
			else {
				throw new IllegalArgumentException("Class list fetcher passed an illegal arguement.");
			}
		}
	};

	private void updateUI() {
		Session s = Session.getInstance();
		boolean hasData = mData != null && !mData.isEmpty();
		
		// Change the label in case this semester isn't addable.
		int label = (Session.getInstance().isAddable()) ? R.string.add_new_class : R.string.search_catalog;
		mFooter.setText(label);
		
	
		// Find the notification bar for later action
		View notificationBar = this.findViewById(R.id.notification_bar);
		
		// Do a quick check and see if we have any new semesters to see.
		ArrayList<String> sids = s.getNewSemesters();
		boolean haveNew = false;
		
		for(String id : sids) {
			boolean seen = BMR.getBooleanPreference(this, BMR.PREFS_SEMESTERS_CHECKED, s.getUsername(), id);
			
			// Shave it off if we have seen it.
			if(seen) {
				sids.remove(id);
			}
			else {
				System.out.println(s.getSemesterName(id) + " is totally new.");
				haveNew = true;
				BMR.setPreference(this, BMR.PREFS_SEMESTERS_CHECKED, s.getUsername(), id, true);
			}
		}
		
		
		// A new semester has become available for adding classes...
		if(haveNew) {
			int msg = (sids.size() > 1) ? R.string.multiple_semester_msg : R.string.single_semester_msg;
			notifyUser(msg, R.string.switch_semester, mSwitchSemesterClickListener);
		}
		
		// If the ListView doesn't have any data and the Semester is addable,
		// let's encourage the user to add a class.
		else if(!hasData && s.isAddable()) {
			notifyUser( R.string.no_classes_msg, R.string.add_new_class, mAddClassClickListener);
		}
		
		// If the Semester isn't confirmed (and the user has added classes + semester is addable)
		// present a way for the user to confirm their schedule.
		else if ( s.isConfirmed() == false && s.isAddable()) {
			notifyUser( R.string.schedule_not_confirmed_msg, R.string.confirm_schedule, mConfirmScheduleClickListener );
		}

		// Just hide the notification bar.
		else {
			notificationBar.setVisibility(View.GONE);
		}
		
		// Update the ListView
		setListAdapter( new SimpleAdapter(
				SemesterActivity.this, mData, R.layout.classitem,
				BMR.CLASSLIST_FIELDS, BMR.CLASSLIST_VIEWS) );
	}

	private void notifyUser(int resMsg, int resBtn,	OnClickListener listener) {
		View notificationBar = this.findViewById(R.id.notification_bar);
		Button actionButton = (Button) this.findViewById(R.id.notification_button);
		TextView msg = (TextView) this.findViewById(R.id.notification_message);
		
		msg.setText(resMsg);
		actionButton.setText(resBtn);
		actionButton.setOnClickListener(listener);
		
		notificationBar.setVisibility(View.VISIBLE);
	}

	public void run() {
		Session s = Session.getInstance();

		try {
			mData = s.fetchCurrentClasses();
			Message msg = new Message();
			msg.arg1 = BMR.MESSAGE_SUCCESS;
			mClassListHandler.sendMessage(msg);
		} catch (BMRException e) {
			Message msg = new Message();
			msg.arg1 = BMR.MESSAGE_FAILURE;
			msg.arg2 = e.getResourceId();
			mClassListHandler.sendMessage(msg);
		}
	}

	private void recache() {
		Session s = Session.getInstance();
		
		if ( s.isCurrentClassListCached() ) {
			try {
				mData = s.fetchCurrentClasses();
				updateUI();
			}
			catch (BMRException e) {
				Message msg = new Message();
				msg.arg1 = 0;
				msg.arg2 = e.getResourceId();
				mClassListHandler.sendMessage(msg);
			}
		}
		else {
			// Hold yer horses please...
			mWaitDialog = BMR.showProgressDialog(this, R.string.please_wait,
					R.string.communicating_with_server);
	
			Thread t = new Thread(this);
			t.start();
		}
	}
}
